import React, { useCallback, useEffect, forwardRef, useImperativeHandle, useRef } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import styles from './index.module.less';

const AudioDance = forwardRef((props, ref) => {
  const { className } = props;

  useImperativeHandle(ref, () => ({
    drawArray: (array) => {
      if (array) {
        clear();
        drawDance(array);
      }
    },
  }));

  const options = useRef({
    rows: 18,
    cols: 46,
    minWidth: 480,
    colors: [
      '#0A77B4',
      '#1084B7',
      '#1691BB',
      '#1C9EBF',
      '#21AAC2',
      '#27B7C6',
      '#2DC4CA',
      '#33D1CD',
      '#39DED1',
      '#3CE5D3',
      '#3CE5D3',
      '#3CE5D3',
      '#3CE5D3',
      '#3CE5D3',
      '#3CE5D3',
      '#3CE5D3',
      '#3BE4D2',
      '#3BE4D2',
    ],
    emptyColor: '#3CE5D310',
  }).current;

  const canRef = useRef(null);
  const containeRef = useRef(null);

  const onResize = useCallback(() => {
    canRef.current.width = containeRef.current.offsetWidth;
    canRef.current.height = containeRef.current.offsetHeight;

    const { height, width } = canRef.current || {};
    options.cols = 48;
    options.blockWidth = Math.max(width / options.cols, 10);
    options.rows = Math.round(height / options.blockWidth);
    options.blockHeight = Math.max(height / options.rows, 10);
    options.gap =
      options.blockWidth < 10
        ? 1
        : options.blockWidth >= 10 && options.blockWidth <= 30
        ? options.blockWidth / 5 - 1
        : 5;
    options.innerRect = { w: options.blockWidth - options.gap * 2, h: options.blockHeight - options.gap * 2 };
  }, []);

  useEffect(() => {
    const ro = new ResizeObserver(onResize);
    containeRef.current && ro.observe(containeRef.current);
    return () => {
      containeRef.current && ro.unobserve(containeRef.current);
      ro.disconnect();
    };
  }, []);

  const rafIdRef = useRef(null);
  useEffect(() => {
    let count = 0;
    let time = Date.now();
    (function loop() {
      const now = Date.now();
      if (now - time > 1000) {
        window.console.log(count);
        count = 0;
        time = now;
      } else {
        count += 1;
      }
      rafIdRef.current = window.requestAnimationFrame(loop);
    })();
    return () => {
      window.cancelAnimationFrame(rafIdRef.current);
    };
  }, []);

  const drawDance = (data) => {
    const context = canRef.current?.getContext('2d');
    if (!context) {
      return;
    }
    const { height } = canRef.current || {};
    const {
      cols,
      rows,
      gap,
      blockWidth,
      blockHeight,
      innerRect: { w, h },
    } = options;
    const step = parseInt(data.length / cols, 10);
    for (let i = 0; i < 48; i += 1) {
      const d = data[i * step];
      const m = parseInt((d / 256) * rows, 10);
      for (let j = 0; j < rows; j += 1) {
        context.fillStyle = options.colors[Math.min(j, options.colors.length)];
        if (j >= m) {
          context.fillStyle = options.emptyColor;
        }
        context.fillRect(i * blockWidth + gap, height - blockHeight * j - blockHeight + gap, w, h);
      }
    }
  };

  const clear = () => {
    const { width, height } = canRef.current || {};
    const context = canRef.current?.getContext('2d');
    if (!context) {
      return;
    }
    context.clearRect(0, 0, width, height);
  };

  return (
    <div ref={containeRef} className={classnames(styles.root, className)} style={{ minWidth: options.minWidth }}>
      <canvas ref={canRef} />
    </div>
  );
});

AudioDance.defaultProps = {
  className: null,
};

AudioDance.propTypes = {
  className: PropTypes.any,
};

export default AudioDance;
